import TextUtils from './TextUtils'

type char = string;
type byte = number;

export default class HexUtil {
  private static readonly DIGITS_LOWER: char[] = ['0', '1', '2', '3', '4', '5',
    '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
  private static readonly DIGITS_UPPER: char[] = ['0', '1', '2', '3', '4', '5',
    '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
  private static readonly UPDATE_HEAD: string = 'xxxxxxxx';
  private static readonly WRITE_PACKET_DATE_START: string = "xxxxxxxxx"; //发送数据固定起始数据域格式
  public static readonly WRITE_PACKET_DATE_END: string = "xxxx"; // 发送数据固定结束数据域格式
  public static readonly NOTIFY_DATE_START: string = "xxxxxxxx"; // 通知数据头
  public static readonly NOTIFY_DATE_END: string = "xxxxxxxxx"; // 通知数据尾

  public static encodeHex(data: byte[], toLowerCase: boolean = true): char[] {
    return HexUtil.encodeHexInner(data, toLowerCase ? HexUtil.DIGITS_LOWER : HexUtil.DIGITS_UPPER);
  }

  protected static encodeHexInner(data: byte[], toDigits: char[]): char[] {
    if (!data)
      return null;
    let l: number = data.length;
    let out: char[] = new Array(l << 1);
    let index:number = 0
    for(let item of data) {
      index += 1
      out[index] = toDigits[(0xF0 & item) >>> 4]
      index += 1
      out[index] = toDigits[0x0F & item]
    }
    return out;
  }

  protected static encodeHexInner2(data: Uint8Array, toDigits: char[]): char[] {
    if (!data)
      return null;
    let l: number = data.length;
    let out: char[] = new Array(l << 1);
    let index:number = 0
    for(let item of data) {
      index += 1
      out[index] = toDigits[(0xF0 & item) >>> 4]
      index += 1
      out[index] = toDigits[0x0F & item]
    }
    return out;
  }

  private static byteToString(data: char[]): string {
    let str = '';
    for(let item of data) {
      str += item
    }
    return str;
  }

  public static encodeHexStr(data: byte[], toLowerCase: boolean = true): string{
    return HexUtil.encodeHexStrInner(data, toLowerCase ? HexUtil.DIGITS_LOWER : HexUtil.DIGITS_UPPER);
  }

  protected static encodeHexStrInner(data: byte[], toDigits: char[]): string {
    return HexUtil.byteToString(HexUtil.encodeHexInner(data, toDigits));
  }

  public static encodeHexStr2(data: Uint8Array, toLowerCase: boolean = true): string{
    return HexUtil.encodeHexStrInner2(data, toLowerCase ? HexUtil.DIGITS_LOWER : HexUtil.DIGITS_UPPER);
  }

  protected static encodeHexStrInner2(data: Uint8Array, toDigits: char[]): string {
    return HexUtil.byteToString(HexUtil.encodeHexInner2(data, toDigits));
  }

  public static formatHexString(data: Uint8Array, addSpace: boolean = false): string {
    if (!data || data.length < 1)
      return null;
    let sb: string = '';
    for (let item of data) {
      let hex: String = (item & 0xFF).toString(16);
      if (hex.length == 1) {
        hex = '0' + hex;
      }
      sb = sb + hex;
      if (addSpace)
        sb = sb + " ";
    }
    return sb;
  }

  public static decodeHex(data: char[]): byte[] {
    let len: number = data.length;

    if ((len & 0x01) != 0) {
      throw new Error("Odd number of characters.");
    }

    let out: byte[] = new Array(len >> 1);
    let i:number = 0
    let j:number = 0
    while(j < len) {
      let f : number = HexUtil.toDigit(data[j], j) << 4
      j += 1
      f = f | HexUtil.toDigit(data[j], j)
      j += 1
      out[i] = (f & 0xFF)
    }
    return out;
  }

  protected static toDigit(ch: char, index: number): number {
    let digit: number = HexUtil.charToByte(ch.toUpperCase()); //Character.digit(ch, 16);
    if (digit == -1) {
      throw new Error("Illegal hexadecimal character " + ch
        + " at index " + index);
    }
    return digit;
  }

  public static hexStringToBytes(hexString: string): Uint8Array {
    if (TextUtils.isEmpty(hexString)) {
      return null;
    }
    hexString = hexString.trim();
    hexString = hexString.toUpperCase();
    let length: number = hexString.length / 2;
    let hexChars: char[] = TextUtils.toCharArray(hexString);
    let d: byte[] = new Array(length);
    let index = 0
    while (index < length) {
      let pos = index * 2;
      d[index] = (HexUtil.charToByte(hexChars[pos]) << 4 | HexUtil.charToByte(hexChars[pos + 1]));
      index += 1
    }
    return new Uint8Array(d);
  }

  public static hexStringToBytes2(hexString: string): Uint8Array {
    if (TextUtils.isEmpty(hexString)) {
      return null;
    }
    hexString = hexString.trim();
    hexString = hexString.toUpperCase();
    let length: number = hexString.length / 2;
    let hexChars: char[] = TextUtils.toCharArray(hexString);
    let d: byte[] = new Array(length);
    let index = 0
    while (index < length) {
      let pos = index * 2;
      d[index] = (HexUtil.charToByte(hexChars[pos]) << 4 | HexUtil.charToByte(hexChars[pos + 1]));
      index += 1
    }
    return new Uint8Array(d);
  }

  public static charToByte(c: char): byte {
    return "0123456789ABCDEF".indexOf(c);
  }

  public static extractData(data: Uint8Array, position: number): String {
    return HexUtil.formatHexString(new Uint8Array([data[position]]));
  }

  public static getWriteDataPacket(hexString: string): string {
    if (TextUtils.isEmpty(hexString) || hexString.length % 2 !== 0) {
      return ''
    }

    let dataField: string = ''
    if (hexString.startsWith(HexUtil.UPDATE_HEAD)) {
      dataField = hexString.replace(HexUtil.UPDATE_HEAD, '')
    } else {
      dataField = HexUtil.WRITE_PACKET_DATE_START.concat(hexString, HexUtil.WRITE_PACKET_DATE_END)
    }
    return dataField
  }

  public static stringToHex(s: string): string {
    let str: string = ''
    let len: number = s.length
    let index: number = 0
    while (index < len) {
      let ch: number = s.charCodeAt(index)
      let s4: string = ch.toString(16)
      str = str + s4
      index += 1
    }
    return str
  }

  public static hexToString(data:string):string {
    let val : string = ''
    let arr:string[] = data.split(',')
    let index:number = 0
    while(index < arr.length) {
      val += String.fromCharCode(parseInt(arr[index], 16))
      index += 1
    }
    let b:string = decodeURIComponent(val)
    console.log('hexToString b' + b)
    return b
  }

  public static  ab2hex(buffer:ArrayBuffer):string {
    var hexArr = Array.prototype.map.call(
      new Uint8Array(buffer),
      function (bit) {
        return ('00' + bit.toString(16)).slice(-2)
      }
    )
    return hexArr.join(',');
  }

  /**
   * 过滤通知消息头和消息尾
   * @param data
   */
  public static filterValue(data:string) : string {
    if (data === null) {
      return ''
    }
    return data.replace(this.NOTIFY_DATE_START, '').replace(this.NOTIFY_DATE_END, '')
  }
}

